<template>
  <div>
    <virtual-header-table
      ref='virtualHeaderTable'
      v-bind="computedAttrs.headAttrs"
      v-on="computedListeners.headListeners"
    />
    <virtual-data-table
      ref='virtualMainTable'
      v-bind="computedAttrs.mainAttrs"
      v-on="computedListeners.mainListeners"
    />
  </div>
</template>

<script>
import { createArray } from "@/utils";
import TableExporter from "../excel-export/index";
import { queryVNodes, queryVirtualTableVNodes } from "../excel-export/utils";
import VirtualHeaderTable from "./virtual-header-table.vue";
import VirtualDataTable from "./virtual-data-table.vue";
export default {
  name: "VirtualTable",

  components: {
    VirtualHeaderTable,
    VirtualDataTable,
  },

  computed: {
    computedAttrs() {
      const attrs = this.$attrs || {};
      const mergeAttrs = { ...attrs, ...this.defineDefaultAttrs() };
      const newAttrs = Object.entries(mergeAttrs).reduce(
        (p, [key, value]) => {
          const isHeadAttrs = key.toLowerCase().startsWith("header-");
          const newKey = isHeadAttrs
            ? key.replace(/header-/, "")
            : key.replace(/main-/, "");
          const obj = isHeadAttrs ? p.headAttrs : p.mainAttrs;
          obj[newKey] = value;
          return p;
        },
        {
          headAttrs: {},
          mainAttrs: {},
        }
      );
      return newAttrs;
    },

    computedListeners() {
      const listeners = this.$listeners || {};
      const mergeListeners = { ...listeners, ...this.defineDefaultEvents() };
      const newListeners = Object.entries(mergeListeners).reduce(
        (p, [key, value]) => {
          const isHeadListeners = key.toLowerCase().startsWith("header-");
          const newKey = isHeadListeners
            ? key.replace(/header-/, "")
            : key.replace(/main-/, "");
          const obj = isHeadListeners ? p.headListeners : p.mainListeners;
          obj[newKey] = value;
          return p;
        },
        {
          headListeners: {},
          mainListeners: {},
        }
      );
      return newListeners;
    },
  },

  data() {
    return {
      visibleCells: [],
      cells: [],
      tableData: {},
    };
  },

  methods: {
    async getInstance() {
      await this.$nextTick();
      const virtualHeaderTable = this.$refs["virtualHeaderTable"];
      const virtualMainTable = this.$refs["virtualMainTable"];
      const virtualHeaderInstance = virtualHeaderTable.getInstance();
      const virtualMainInstance = virtualMainTable.getInstance();
      return {
        virtualTableInstance: this,
        virtualHeaderInstance,
        virtualMainInstance,
      };
    },

    initTable() {
      return {
        columns: [],
        data: [],
        mergeCells: [],
      };
    },

    // 手动控制加载表格
    /**
     *
     * @param {*} tableData 表格数据
     * {
     *  headerTableData:{
     *    columns:[], // 列,
     *    data:[], // 头部表格数据
     *    mergeCells:[] // 合并单元格信息
     *  },
     *  mainTableData:{
     *    columns:[], // 列
     *    data:[], // 内容表格数据
     *    mergeCells:[] // 合并单元格信息
     *  }
     * }
     */
    async loadTable(tableData = {}) {
      const [headerComponent, mainComponent] = this.$children;
      const {
        headerTableData = this.initTable(),
        mainTableData = this.initTable(),
      } = tableData;
      await headerComponent.init(headerTableData);
      await mainComponent.init(mainTableData);
      this.tableData = tableData;
    },

    // excel单元格样式
    getExcelCellStyle(tdNode) {
      const background = (tdNode.data.style || {}).backgroundColor;
      return {
        alignment: { vertical: "middle", horizontal: "center", wrapText: true },
        border: {
          top: { style: "thin", color: { argb: "FF333333" } },
          left: { style: "thin", color: { argb: "FF333333" } },
          bottom: { style: "thin", color: { argb: "FF333333" } },
          right: { style: "thin", color: { argb: "FF333333" } },
        },
        fill: {
          type: "pattern",
          pattern: "solid",
          fgColor: {
            argb: background ? background.replace(/#/, "") : "",
          },
        },
      };
    },

    // excel行样式
    getExcelRowStyle() {
      return {
        height: 30,
      };
    },

    // excel列样式
    getExcelColumnStyle() {
      const instance = this.$children[0];
      const len = instance.getColumnsLen();
      return createArray(len, () => {
        return { width: 20 };
      });
    },

    // 处理需要导出的数据
    handleExportData(options, tableData) {
      const {
        sheetName = "chenying-excel-sheet1",
        insertHeaderData = [],
        insertFooterData = [],
        getRowStyle = this.getExcelRowStyle,
        getCellStyle = this.getExcelCellStyle,
        cloumnsStyle = this.getExcelColumnStyle(),
      } = options;
      const datas = [...insertHeaderData, ...tableData, ...insertFooterData];
      return {
        sheetName,
        datas,
        getCellStyle,
        getRowStyle,
        cloumnsStyle,
      };
    },

    // 真正导出(内部使用)
    async export(options, tables) {
      const { progress = () => {}, excelName = "chenying-excel.xlsx" } = options;
      const exporter = new TableExporter({
        tables,
        progress,
      });
      await exporter.export();
      await exporter.download(excelName);
    },

    // 将虚拟表格导出excel(外部使用)
    async exportExcel(options = {}) {
      /* -------------关闭虚拟滚动，重新加载数据-------------- */
      await this.closeVirtualScroll();
      await this.loadTable(this.tableData);

      /* -------------查找对应的虚拟节点-------------- */
      const [headerComponent, mainComponent] = this.$children;
      const headerInstance = headerComponent.getInstance();
      const mainInstance = mainComponent.getInstance();
      const headerVNode = queryVirtualTableVNodes(headerInstance);
      const mainVNode = queryVirtualTableVNodes(mainInstance);
      const headerTrs = queryVNodes(headerVNode, (node) => node.tag === "tr");
      const mainTrs = queryVNodes(mainVNode, (node) => node.tag === "tr");
      const datas = [...headerTrs, ...mainTrs];

      /* -------------导出数据-------------- */
      const tables = this.handleExportData(options, datas);
      await this.export(options, tables);

      /* -------------开启虚拟滚动，重新加载数据-------------- */
      await this.openVirtualScroll();
      await this.loadTable(this.tableData);
    },

    async closeVirtualScroll() {
      const [headerComponent, mainComponent] = this.$children;
      await headerComponent.closeVirtualScroll();
      await mainComponent.closeVirtualScroll();
    },

    async openVirtualScroll() {
      const [headerComponent, mainComponent] = this.$children;
      await headerComponent.openVirtualScroll();
      await mainComponent.openVirtualScroll();
    },

    // 定义默认属性
    defineDefaultAttrs() {
      return {};
    },

    // 定义默认事件
    defineDefaultEvents() {
      return {
        "main-scroll": this.handleScroll, // 身体表格滚动事件
      };
    },

    // 同步滚动
    async handleScroll() {
      const instanceInfo = await this.getInstance();
      const { virtualHeaderInstance, virtualMainInstance } = instanceInfo;
      if (virtualHeaderInstance && virtualMainInstance) {
        const scrollLeft = virtualMainInstance.getScroll().scrollLeft;
        virtualHeaderInstance.scrollTo(scrollLeft, null);
      }
    },
  },
};
</script>

<style>
</style>
